package cz.zcu.fav.rat.security.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.apache.log4j.Logger;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import cz.zcu.fav.rat.beans.User;
import cz.zcu.fav.rat.security.beans.UserDetailsBean;

public class KerberosUserDetailsProvider extends AbstractUserLoginMapper
		implements AuthenticationProvider
{
	private static final Logger logger = Logger
			.getLogger(KerberosUserDetailsProvider.class);

	private String implementor;
	protected String jaasConfig;
	protected String krb5Config;
	protected String jaasContextName;
	protected boolean localPasswordRequired;

	public boolean isLocalPasswordRequired()
	{
		return localPasswordRequired;
	}

	public void setLocalPasswordRequired(boolean localPasswordRequired)
	{
		this.localPasswordRequired = localPasswordRequired;
	}

	public void setJaasConfig(String jaasConfig)
	{
		this.jaasConfig = jaasConfig;
	}

	public void setKrb5Config(String krb5Config)
	{
		this.krb5Config = krb5Config;
	}

	public void setJaasContextName(String jaasContextName)
	{
		this.jaasContextName = jaasContextName;
	}

	public void setImplementor(String implementor)
	{
		this.implementor = implementor;
	}

	@Override
	protected void init()
	{
		super.init();
		java.net.URL urlJaas = KerberosUserDetailsProvider.class
				.getClassLoader().getResource("jaas.conf");
		java.net.URL urlkrb5 = KerberosUserDetailsProvider.class
				.getClassLoader().getResource("krb5.conf");

		System.setProperty("java.security.auth.login.config", urlJaas.getPath());
		System.setProperty("java.security.krb5.conf", urlkrb5.getPath());

	}

	@Override
	protected LoginImplementor getImplementor()
	{
		return new LoginImplementor(implementor, localPasswordRequired);
	}

	private boolean doAuthenticateUser(String username, String password)
	{
		LoginContext lc = null;
		try
		{
			lc = new LoginContext(jaasContextName, new CallbackHandlerImpl(
					username, password));
			lc.login();
		} catch (LoginException ex)
		{
			logger.debug("Authentication of user " + username + " failed");
			return false;
		}
		logger.debug("Authentication of user " + username
				+ " was succesfully completed");

		return true;
	}

	private class CallbackHandlerImpl implements CallbackHandler
	{

		private String userName;

		private String password;

		private CallbackHandlerImpl(String userName, String password)
		{
			this.userName = userName;
			this.password = password;
		}

		@Override
		public void handle(Callback[] callbacks) throws IOException,
				UnsupportedCallbackException
		{
			for (int i = 0; i < callbacks.length; i++)
			{
				Callback cb = callbacks[i];
				if (cb instanceof NameCallback)
				{
					NameCallback ncb = (NameCallback) cb;
					ncb.setName(userName);
				} else if (cb instanceof PasswordCallback)
				{
					PasswordCallback pcb = (PasswordCallback) cb;
					pcb.setPassword(password.toCharArray());

				}
			}
		}

	}

	@Override
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException
	{
		User user = getUser(authentication.getName());
		doAuthenticateUser(user.getNickname(),
				(String) authentication.getCredentials());

		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		authorities.add(new SimpleGrantedAuthority(user.getRole().toString()));

		UserDetailsBean userDetails = new UserDetailsBean();
		List<String> roleList = new ArrayList<String>();
		roleList.add(user.getRole().toString());

		userDetails.setAuthorities(roleList);
		userDetails.setUserName(user.getNickname());
		userDetails.setPassword(user.getPasshash());
		userDetails.setId(user.getId());
		userDetails.setImplementor(user.getImplementor());

		return new UsernamePasswordAuthenticationToken(userDetails,
				authentication, authorities);
	}

	@Override
	public boolean supports(Class<?> authentication)
	{
		return authentication.equals(UsernamePasswordAuthenticationToken.class);
	}

}
